//
// Created by LiuYou on 2021/6/18.
//

/*
 * main.cpp完全来模拟用户的操作。
 */
/*
 * MyTBF2这个目录下的文件实现了令牌桶的库编写, 而且在慧琴老师的带领下我成功编写出来。
 * 这是C风格的编程习惯和思想。之后可以对这些文件进行重构。
 */

#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <csignal>
#include <cstring>
#include "MyTBF.hpp"

#define CPS         10
#define BUFSIZE     1024
#define BURST       100

int main(int argc, char** argv) {
    if (argc < 2) {
        std::fprintf(stderr, "Usage: ...\n");
        std::exit(1);
    }

    mytbf_t* tbf = mytbf_init(CPS, BURST);
    if (tbf == nullptr) {
        std::fprintf(stderr, "mytbf_init()函数调用失败！\n");
        std::exit(1);
    }

    int sFD{};
    do {
        sFD = open(argv[1], O_RDONLY);
        if (sFD < 0) {
            if (errno != EINTR) {
                std::perror("open()");
                std::exit(1);
            }

        }
    } while (sFD < 0);

    int dFD{1};

    // 这个程序效率的核心问题在这里 BUF_SIZE的长度, 对这个程序的效率影响非常大！！！！
    char buf[BUFSIZE]{};
    int pos{};
    for (;;) {

        // TODO 注意: 这里实现了令牌桶的设计。
        int size = mytbf_fetchtoken(tbf, BUFSIZE);
        if (size < 0) {
            std::fprintf(stderr, "mytbf_fetchtoken(): %s\n", strerror(-size));
            std::exit(1);
        }

#if false
        // TODO 注意: 重要: 这里的 continue有BUG。改进在另外一个分支中。

        // 但是没有必要坚持读够sizeof(buf)个字节, 因为文件本身可能就没有这么多个字节数
        // 所以, 采取的策略是有多少个字节就读多少个字节。
        ssize_t len = read(sFD, buf, sizeof(buf));
        if (len < 0) {
            if (errno == EINTR) {
                continue;
            }
            std::perror("read()");
            break;
        }
        if (len == 0) {
            break;
        }
#else
        // TODO 注意: 改进版
        //  说真的这个BUG, 我是真的发现不了, 而且即使发现了我也不会改！
        ssize_t len{};
        // TODO 注意: 这里就体现了 漏桶！
        while ((len = read(sFD, buf, size)) < 0) {
            if (errno == EINTR)
                continue;
            std::perror("read()");
            break;
        }
        if (len == 0) {
            break;
        }

#endif

        if (size - len > 0) {
            mytbf_returntoken(tbf, size - len);
        }

        // TODO 注意: 问题在这里, 逻辑错误了, 我没有写 pos = 0; 这条语句！
        // :(
        pos = 0;
        // 坚持写够len个字节
        while (len > 0) {
            ssize_t ret = write(dFD, buf + pos, len);
            if (ret < 0) {
                if (errno == EINTR) {
                    continue;
                }
                std::perror("write()");
                // 这里其实是有内存泄漏的。
                std::exit(1);
            }
            // 这里很精妙
            len -= ret;
            pos += ret;
        }

    }


    close(sFD);

    mytbf_destroy(tbf);

    std::exit(0);
}
